package elitequant.event;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentLinkedQueue;

import elitequant.data.DataFeedBase;

/**
 * Event queue + a while loop to dispatch events
 * 
 * @author btcaimail@163.com
 *
 */
public class BacktestEventEngine extends EventsEngine implements Runnable {

	private DataFeedBase dataFeed; // pull from backtest data feed

	private boolean active;

	private ConcurrentLinkedQueue<Event> queue;

	private Map<EventType, List<EventHandler>> handlers;

	private List<EventHandler> generalHandlers;

	public BacktestEventEngine(DataFeedBase dataFeed) {
		this.dataFeed = dataFeed;
		active = true;
		queue = new ConcurrentLinkedQueue<Event>();
		handlers = new HashMap<EventType, List<EventHandler>>();
		generalHandlers = new ArrayList<EventHandler>();
	}

	/**
	 * run backtest
	 */
	@Override
	public void run() {
		System.out.println("Running Backtest...");
		while (active) {
			Event event = queue.poll();
			if (event != null) {
				List<EventHandler> targetHandlers = handlers.get(event.getEventType());

				if (targetHandlers != null) {
					for (EventHandler handler : targetHandlers) {
						try {
							handler.handle(event);
						} catch (Exception e) {
							e.printStackTrace();
							System.out.println(String.format("Error %s", e.getMessage()));
						}
					}
				}

				for (EventHandler handler : generalHandlers) {
					try {
						handler.handle(event);
					} catch (Exception e) {
						System.out.println(String.format("Error %s", e.getMessage()));
					}
				}
			} else { // empty
				try {
					event = dataFeed.streamNext();
					put(event);
				} catch (Exception e) {
					// stop if not able to next event
					active = false;
				}

			}
		}

	}

	@Override
	public void put(Event event) {
		queue.add(event);

	}

	public synchronized void registerHandler(EventType eventType, EventHandler handler) {
		List<EventHandler> targetHandlers = handlers.get(eventType);
		if (targetHandlers == null) {
			targetHandlers = new ArrayList<EventHandler>();
			handlers.put(eventType, targetHandlers);
		}
		targetHandlers.add(handler);

	}

	public synchronized void unregisterHandler(EventType eventType, EventHandler handler) {
		List<EventHandler> targetHandlers = handlers.get(eventType);
		if (targetHandlers != null) {
			targetHandlers.remove(handler);
		}

	}

	public synchronized void registerGeneralHandler(EventHandler handler) {
		generalHandlers.add(handler);

	}

	public synchronized void unregisterGeneralHandler(EventHandler handler) {
		generalHandlers.remove(handler);

	}

}
